package query_handler

import (
	"crypto/sha256"
	"encoding/hex"
	"file-cleaner/internal/logger"
	"file-cleaner/internal/pkg/file_cleaner/file_info"
	"os"
	"syscall"
	"time"
)

type QueryHandler interface {
	doHandle(info *file_info.FileInfo) bool
}

type QueryByATimeImpl struct {
	AfterTime  time.Time
	BeforeTime time.Time
}

type QueryByCTimeImpl struct {
	AfterTime  time.Time
	BeforeTime time.Time
}

type QueryByMTimeImpl struct {
	AfterTime  time.Time
	BeforeTime time.Time
}

func (a QueryByATimeImpl) doHandle(info *file_info.FileInfo) bool {
	stat, ok := info.Sys().(*syscall.Win32FileAttributeData)
	if !ok {
		logger.Warnf("Failed to parse stat from FileInfo: %v", info)
		return false
	}
	atime := stat.LastAccessTime
	//tt := time.Unix(atime.Sec, atime.Nsec)
	tt := time.Unix(0, atime.Nanoseconds())
	return tt.After(a.AfterTime) && tt.Before(a.BeforeTime)
}

func (c QueryByCTimeImpl) doHandle(info *file_info.FileInfo) bool {
	stat, ok := info.Sys().(*syscall.Win32FileAttributeData)
	if !ok {
		logger.Warnf("Failed to parse stat from FileInfo: %v", info)
		return false
	}
	ctime := stat.CreationTime
	//tt := time.Unix(ctime.Sec, ctime.Nsec)
	tt := time.Unix(0, ctime.Nanoseconds())
	return tt.After(c.AfterTime) && tt.Before(c.BeforeTime)
}

func (b QueryByMTimeImpl) doHandle(info *file_info.FileInfo) bool {
	stat, ok := info.Sys().(*syscall.Win32FileAttributeData)
	if !ok {
		logger.Warnf("Failed to parse stat from FileInfo: %v", info)
		return false
	}
	mtime := stat.LastWriteTime
	//tt := time.Unix(mtime.Sec, mtime.Nsec)
	tt := time.Unix(0, mtime.Nanoseconds())
	return tt.After(b.AfterTime) && tt.Before(b.BeforeTime)
}

type QueryHashImpl struct {
}

func (h QueryHashImpl) doHandle(info *file_info.FileInfo) bool {
	bfile, err := os.ReadFile(info.Path())
	if err != nil {
		logger.Error(err)
		return false
	}
	info.SetHash(h.sha256Hash(bfile))
	return true
}

func (h QueryHashImpl) sha256Hash(data []byte) string {
	m := sha256.New()
	m.Write(data)
	return hex.EncodeToString(m.Sum(nil))
}

type QueryForDeleteOnlyImpl struct {
}

func (d QueryForDeleteOnlyImpl) doHandle(info *file_info.FileInfo) bool {
	err := info.SetTag(file_info.OnlyDelete)
	if err != nil {
		logger.Errorf("Failed to set tag for file: %v, cased by: %v", info.Path(), err)
		return false
	}
	info.SetHash("OnlyDelete")
	return true
}
